_Pragma("once");

#include <unistd.h>

#include <sstream>
#include <memory>
#include <mutex>
#include <sys/signal.h>
#include <pthread.h>
#include <condition_variable>

#include "DummyChannel.h"
#include "Protocol.h"

class SpiChannel : public DummyChannel {
public:
    static constexpr std::size_t HEADER_LENGTH = sizeof (LightweightHeader_t) + sizeof (DeviceInfoHeader_t);
    static constexpr std::size_t DATA_OFFSET = 2;

    /** Default constructor */
    SpiChannel(const int channel, DeviceCommunicator& communicator) : DummyChannel(channel, communicator), mpcurrentResult(nullptr) {
        snprintf(mchannelString, sizeof (mchannelString), "%04hx", mchannel);
    }
    /** Default destructor */
    virtual ~SpiChannel() {
    }

    virtual ssize_t read(std::string& result) {
        ssize_t returnCode = mcommunicator.read(mexceptionTip, mcommunicator.mbuffer, sizeof (mcommunicator.mbuffer));
        if (HEADER_LENGTH > returnCode) {
            BUILD_EXCEPTION_PRINTF(FirmwareError::CHANNEL_COMM_ERROR, "%s:read data failed,returnCode:%zd", mexceptionTip.data(), returnCode);
        }
        result.assign(reinterpret_cast<typename std::string::const_pointer>(mcommunicator.mbuffer + HEADER_LENGTH), returnCode - HEADER_LENGTH);
        return returnCode;
    }

    virtual ssize_t write(const std::string& data) {
        const std::size_t dataSize =  data.size();
        const std::size_t length = dataSize + HEADER_LENGTH;
        char buffer[length];

        LightweightHeader_t* pheader = reinterpret_cast <LightweightHeader_t*>(buffer);
        pheader->beginFlag[0] = '@';
        pheader->beginFlag[1] = '@';
        pheader->length = dataSize + sizeof (DeviceInfoHeader_t);
        pheader->reserved1 = 0;

        DeviceInfoHeader_t* pdeviceHeader = reinterpret_cast <DeviceInfoHeader_t*>(pheader->payload);
        pdeviceHeader->channel = mchannel;

//        memcpy(buffer, mchannelString, 4);
//        snprintf(buffer + 4, sizeof (mchannelString), "%04hx", dataSize);

        memcpy(buffer + HEADER_LENGTH, data.data(), dataSize);
        ssize_t result = mcommunicator.write(mexceptionTip, buffer, sizeof (buffer));

        if (0 > result) {
            BUILD_EXCEPTION_PRINTF(FirmwareError::CHANNEL_COMM_ERROR, "%s:write data failed,returnCode:%zd", mexceptionTip.data(), result);
        }

        return result;
    }


    std::string& sendStringReturnString(const std::string& parameter, std::string& result) override;

    bool feedbackData(DeviceInfoHeader_t* pdeviceHeader, size_t length);

    DummyChannel::Mutex& getMutex() override {
        return mallChannelMutex;
    }

    static DeviceInfoHeader_t* waitAndRead(const std::string& exceptionTip, const timespec* timeout, DeviceCommunicator& communicator, void* buffer, ssize_t& length);

    static void initSignalEnv();

protected:

    char mchannelString[5];
    std::condition_variable_any mchannelCondition;
    std::string*volatile mpcurrentResult;

    static Mutex mallChannelMutex;
    static BasicDevice msignalDevice;
    static  sigset_t sigset;
    static siginfo_t siginfo;
};

